
#include <BOpenGL>
#include <BImage>
#include <BCircle>
#include <BGeoSphere>
#include <BViewer>
#include <BLight>
#include "member_BLight.h"
#include "member_BNode.h"

using namespace BWE;

#define member					(*(member_BLight*)_ptr)
#define member_allocate()		_ptr = new member_BLight(this)
#define member_release()		delete (member_BLight*)_ptr

BLight::BLight()
{
	member_allocate();
	this->setAmbient(30, 30, 30);
	this->setDiffuse(255, 255, 255);
	this->setSpecular(128, 128, 128);
}
BLight::BLight(const BString& name)
{
	member_allocate();
	this->setName(name);
	this->setAmbient(30, 30, 30);
	this->setDiffuse(255, 255, 255);
	this->setSpecular(128, 128, 128);
}
BLight::~BLight()
{
	member_release();
}

bool BLight::setMatrix(const BMatrix& matrix)
{
	if (BNode::setMatrix(matrix))
	{
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}

void BLight::setAmbient(BByte r, BByte g, BByte b, BByte a)
{
	setAmbient(BColor(r, g, b, a));
}
void BLight::setAmbient(const BColor& ambient)
{
	if (member.ambient != ambient)
	{
		member.ambient = ambient;
		member.ambientColor.r() = ambient.r() / 255.0f;
		member.ambientColor.g() = ambient.g() / 255.0f;
		member.ambientColor.b() = ambient.b() / 255.0f;
		member.ambientColor.a() = ambient.a() / 255.0f;
		emit(Signal_Freshed);
	}
}
const BColor& BLight::ambient() const
{
	return member.ambient;
}

void BLight::setDiffuse(BByte r, BByte g, BByte b, BByte a)
{
	setDiffuse(BColor(r, g, b, a));
}
void BLight::setDiffuse(const BColor& diffuse)
{
	if (member.diffuse != diffuse)
	{
		member.diffuse = diffuse;
		member.diffuseColor.r() = diffuse.r() / 255.0f;
		member.diffuseColor.g() = diffuse.g() / 255.0f;
		member.diffuseColor.b() = diffuse.b() / 255.0f;
		member.diffuseColor.a() = diffuse.a() / 255.0f;
		emit(Signal_Freshed);
	}
}
const BColor& BLight::diffuse() const
{
	return member.diffuse;
}

void BLight::setSpecular(BByte r, BByte g, BByte b, BByte a)
{
	setSpecular(BColor(r, g, b, a));
}
void BLight::setSpecular(const BColor& specular)
{
	if (member.specular != specular)
	{
		member.specular = specular;
		member.specularColor.r() = specular.r() / 255.0f;
		member.specularColor.g() = specular.g() / 255.0f;
		member.specularColor.b() = specular.b() / 255.0f;
		member.specularColor.a() = specular.a() / 255.0f;
		emit(Signal_Freshed);
	}
}
const BColor& BLight::specular() const
{
	return member.specular;
}

void BLight::setAttenuation(float attenuation)
{
	if (member.attenuation != attenuation)
	{
		member.attenuation = attenuation;
		emit(Signal_Freshed);
	}
}
float BLight::attenuation() const
{
	return member.attenuation;
}

void BLight::setAttenuation2(float attenuation2)
{
	if (member.attenuation2 != attenuation2)
	{
		member.attenuation2 = attenuation2;
		emit(Signal_Freshed);
	}
}
float BLight::attenuation2() const
{
	return member.attenuation2;
}

void BLight::setExponent(float exponent)
{
	if (member.exponent != exponent)
	{
		member.exponent = exponent;
		emit(Signal_Freshed);
	}
}
float BLight::exponent() const
{
	return member.exponent;
}

void BLight::setCutoff(float cutoff)
{
	if (member.cutoff != cutoff)
	{
		member.cutoff = cutoff;
		member.dirty = true;
		this->dirty();
	}
}
float BLight::cutoff() const
{
	return member.cutoff;
}

void BLight::setRadius(BReal radius)
{
	if (member.radius != radius)
	{
		member.radius = radius;
		member.dirty = true;
		this->dirty();
	}
}
BReal BLight::radius() const
{
	return member.radius;
}

const BFrustum& BLight::frustum() const
{
	return member.frustum;
}
const BVector& BLight::direct() const
{
	if (member.dirty)
		member.freshFrustum();
	return member.direct;
}
const BVector& BLight::right() const
{
	if (member.dirty)
		member.freshFrustum();
	return member.right;
}

bool BLight::capture(BImage* image)
{
	if (!image || member.shadowSize == 0)
		return false;
	image->setFormat(Format_Float);
	image->setSize(member.shadowSize);
	BByteArray* pixels = image->pixels();
	glReadPixels(0, 0, member.shadowSize, member.shadowSize, GL_DEPTH_COMPONENT, GL_FLOAT, pixels->data());
	image->dirty();
	return true;
}
bool BLight::capture(BImage* image, const BRect& rect)
{
	if (rect.empty())
		return false;
	if (!image || member.shadowSize == 0)
		return false;
	image->setFormat(Format_Float);
	image->setSize(rect.size());
	int y = member.shadowSize - rect.y() - rect.height();
	BByteArray* pixels = image->pixels();
	glReadPixels(rect.x(), y, rect.width(), rect.height(), GL_DEPTH_COMPONENT, GL_FLOAT, pixels->data());
	image->dirty();
	return true;
}

void BLight::fresh()
{
	//member.refresh = true;
	emit(Signal_Freshed);
}
BBox BLight::flush() const
{
	if (member.dirty)
		member.freshFrustum();
	BSpace space;
	if (BShape* shape = node_member(this)->shapes.first())
	{
		BBox box = shape->box() * shape->matrix();
		space.reset(box.center());
		for (int i = 0; i < node_member(this)->shapes.size(); i++)
		{
			BShape* shape = node_member(this)->shapes[i];
			BBox box = shape->box() * shape->matrix();
			space.expand(box.space());
		}
	}
	if (member.radius > REAL_EPSILON)
	{
		if (member.cutoff < 90)
		{
			float cutoff = bDTR(member.cutoff);
			BReal radius = member.radius * sin(cutoff);
			BReal height = member.radius * cos(cutoff);
			BCircle circle(member.direct * height, radius, member.direct);
			space.expand(circle.space());
			space.expand(member.direct * member.radius);
		}
	}
	if (space.empty() && member.direct.valid())
	{
		space.expand(member.direct);
	}
	return space;
}

bool BLight::query(const BString& text, BValue& value) const
{
	if (member.dirty)
	{
		member.freshFrustum();
	}
	if (text == "frustum")
	{
		value = member.frustum;
		return true;
	}
	if (text == "visual-pairs")
	{
		BArray<BNodeShapePair>& visualPairs = value.fill<BArray<BNodeShapePair>>();
		visualPairs.reset();
		for (int i = 0; i < member.shapeArray.size(); i++)
		{
			ShapeInfo& sinfo = member.shapeArray[i];
			BNodeShapePair& pair = visualPairs.append();
			pair.set(sinfo.node, sinfo.shape);
		}
		return true;
	}
	if (text == "visual-transparent-pairs")
	{
		BArray<BNodeShapePair>& visualTransparentPairs = value.fill<BArray<BNodeShapePair>>();
		visualTransparentPairs.reset();
		for (auto it = member.transShapeQueue.begin(); it.valid(); it++)
		{
			ShapeInfo& sinfo = *it;
			BNodeShapePair& pair = visualTransparentPairs.append();
			pair.set(sinfo.node, sinfo.shape);
		}
		return true;
	}
	if (text == "scene-items")
	{
		ConstSceneOctTreeItemArray& sceneItems = value.fill<ConstSceneOctTreeItemArray>();
		sceneItems.reset();
		sceneItems.append(member.sceneItems);
		return true;
	}
	return BNode::query(text, value);
}
